package com.ds.infrastructure.hyperspace.console.service.impl;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.annotation.JSONField;
import com.ds.infrastructure.hyperspace.console.config.auth.AuthConfig;
import com.ds.infrastructure.hyperspace.console.service.inf.IAuthService;
import com.ds.infrastructure.hyperspace.console.utils.FastJsonUtil;
import com.ds.infrastructure.hyperspace.console.utils.HttpUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.fluent.Request;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * 权限管理
 *
 * @author: yaozhendong
 * @create: 2019-11-18 16:00
 **/
@Slf4j
@Service
public class AuthService implements IAuthService {
    private static final String ERROR_IO_EXCEPTION = "IOException:";
    private static final String ERROR_JSON_EXCEPTION = "JSONException:";
    private static final String ERROR_EXCEPTION = "Exception:";
    @Autowired
    AuthConfig authConfig;

    @Override
    public AuthInfo validateAuth(String httpMethod, String uri, String userId, String userName) {
        if (isTmpAuthUser(userName)) {
            AuthInfo tmpAuth = new AuthInfo();
            tmpAuth.setValidated(true);
            return tmpAuth;
        }
        String requestUUID = UUID.randomUUID().toString();
        String url = String.format("%s/%s?method=%s&uri=%s&user=%s", getAuthUrlPath(authConfig.getAuthPath().getValidatePath()), authConfig.getApp(), httpMethod.toUpperCase(), uri, userId);
        try {
            log.info(String.format("Request UUID:%s;User Name:%s;User Id:%s;uri:%s;  URL:%s;",
                    requestUUID, userName, userId, uri, url));
            String response = HttpUtil.getRequest(url).execute().handleResponse((ResponseHandler<String>) httpResponse -> EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8));
            log.info(String.format("Request UUID:%s;Response Body:%s;", requestUUID, response));
            AuthInfo authInfo = FastJsonUtil.deserialize(response, AuthInfo.class);
            if (authInfo == null) {
                throw new UnauthenticatedException("Auth:验证权限失败，找到有效的信息");
            }
            return authInfo;
        } catch (IOException e) {
            log.error(getErrorDesc(requestUUID, ERROR_IO_EXCEPTION), e);
            throw new UnauthenticatedException("Auth:验证权限失败，IOException:" + e.getMessage());
        } catch (JSONException e) {
            log.error(getErrorDesc(requestUUID, ERROR_JSON_EXCEPTION), e);
            throw new UnauthenticatedException("Auth:验证权限失败，JSONException:" + e.getMessage());
        } catch (UnauthenticatedException e) {
            throw new UnauthenticatedException(e.getMessage());
        } catch (Exception e) {
            log.error(getErrorDesc(requestUUID, ERROR_EXCEPTION), e);
            throw new UnauthenticatedException("Auth:验证权限失败，Exception:" + e.getMessage());
        }
    }

    /**
     * 紧急情况下(如：Auth服务不可用时)，临时授权用户
     *
     * @param userName
     * @return
     */
    private boolean isTmpAuthUser(String userName) {
        if (StringUtils.isBlank(authConfig.getTmpAuthUsers())) {
            return false;
        }
        List<String> userList = Arrays.asList(authConfig.getTmpAuthUsers().split(","));
        if (userList.contains(userName)) {
            log.warn("Auth服务不可用时，紧急情况下，临时授权用户,user name={}", userName);
            return true;
        }
        return false;
    }

    private String getErrorDesc(String requestUUID, String exceptionType) {
        return String.format("Request UUID:%s;%s", requestUUID, exceptionType);
    }

    private String getAuthUrlPath(String path) {
        return authConfig.getAuthUrl() + path;
    }

    @Data
    public static class AuthInfo {
        @JSONField(name = "appid")
        private String appId;
        private String sid;
        private String uri;
        private String method;
        @JSONField(name = "is_validated")
        private boolean isValidated;
        private String id;
    }
}
